#ifdef PRECOMPILEDHEADERS
	#include "Tactical All.h"
#else
	#include "Types.h"
	#include "ArmsDealerInvInit.h"
	#include "Item Types.h"
	#include "Arms Dealer Init.h"
	#include "DEbug.h"
#endif

#include "tchar.h"
#include "windowsx.h"

extern INT8	gbSelectedArmsDealerID;

// This table controls the order items appear in inventory at BR's and dealers, and which kinds of items are sold used
ITEM_SORT_ENTRY DealerItemSortInfo[ ] =
{
//  item class					weapon class	sold used?
	{ IC_GUN,							HANDGUNCLASS, TRUE	},
	{ IC_GUN,							SHOTGUNCLASS, TRUE	},
	{ IC_GUN,							SMGCLASS,			TRUE	},
	{ IC_GUN,							RIFLECLASS,		TRUE	},
	{ IC_GUN,							MGCLASS,			FALSE	},
	{ IC_GUN,							MOUNTEDCLASS,			FALSE	},
	{ IC_LAUNCHER,				NOGUNCLASS,		FALSE	},
	{ IC_AMMO,						NOGUNCLASS,		FALSE	},
	{ IC_GRENADE,					NOGUNCLASS,		FALSE	},
	{ IC_BOMB,						NOGUNCLASS,		FALSE	},
	{ IC_BLADE,						NOGUNCLASS,		FALSE	},
	{ IC_THROWING_KNIFE,	NOGUNCLASS,		FALSE	},
	{ IC_PUNCH,						NOGUNCLASS,		FALSE	},
	{ IC_ARMOUR,					NOGUNCLASS,		TRUE	},
	{ IC_FACE,						NOGUNCLASS,		TRUE	},
	{ IC_MEDKIT,					NOGUNCLASS,		FALSE	},
	{ IC_KIT,							NOGUNCLASS,		FALSE	},
	{ IC_MISC,						NOGUNCLASS,		TRUE	},
	{ IC_THROWN,					NOGUNCLASS,		FALSE	},
	{ IC_KEY,							NOGUNCLASS,		FALSE	},

	// marks end of list
	{ IC_MONEY,							NOGUNCLASS,		FALSE	},
	{ IC_NONE,						NOGUNCLASS,		},
};



//
// Setup the inventory arrays for each of the arms dealers
//
//	The arrays are composed of pairs of numbers
//		The first is the item index
//		The second is the amount of the items the dealer will try to keep in his inventory

UINT32 gDealerInvCount[NUM_ARMS_DEALERS];
DEALER_POSSIBLE_INV gDealerInventory[NUM_ARMS_DEALERS][MAXITEMS];

//<SB>
#define DEALER_TAG		_T("DEALER")
#define DEALERITEM_TAG	_T("DEALERITEM")

int GetDealerIniSettings(TCHAR * tsIniPath, HWND hConsole)
{
	int i;
	int npc = 0;
	int iDealer;
	TCHAR tsFullIniPath[MAX_PATH];
	TCHAR tsSectionName[32] = DEALER_TAG;
	_tcscpy(tsFullIniPath,tsIniPath);
	_tcscat(tsFullIniPath,_T("\\dealer.ini"));
	memset(gDealerInvCount,0,sizeof(gDealerInvCount));
	// Get dealers
	Edit_ReplaceSel(hConsole,"\r\nloading_dealers");
	for( i=0; i<NUM_ARMS_DEALERS; i++)
	{
		_itot( i, tsSectionName+_tcslen(DEALER_TAG), 10);
		ArmsDealerInfo[i].dBuyModifier = (float)GetPrivateProfileInt(tsSectionName,_T("buy_speed"),100, tsFullIniPath)/100;
		ArmsDealerInfo[i].dSellModifier = (float)GetPrivateProfileInt(tsSectionName,_T("sell_cost"),100, tsFullIniPath)/100;
		ArmsDealerInfo[i].ubShopKeeperID = GetPrivateProfileInt(tsSectionName,_T("npc"),0, tsFullIniPath);
		ArmsDealerInfo[i].ubTypeOfArmsDealer = GetPrivateProfileInt(tsSectionName,_T("type"),0, tsFullIniPath);
		ArmsDealerInfo[i].iInitialCash = GetPrivateProfileInt(tsSectionName,_T("cash"),0, tsFullIniPath);
		ArmsDealerInfo[i].uiFlags = 0;
		ArmsDealerInfo[i].uiFlags |= GetPrivateProfileInt(tsSectionName,_T("onlyuseditems"),0, tsFullIniPath)?ARMS_DEALER_ONLY_USED_ITEMS:0;
		ArmsDealerInfo[i].uiFlags |= GetPrivateProfileInt(tsSectionName,_T("giveschange"),0, tsFullIniPath)?ARMS_DEALER_GIVES_CHANGE:0;
		ArmsDealerInfo[i].uiFlags |= GetPrivateProfileInt(tsSectionName,_T("acceptsgifts"),0, tsFullIniPath)?ARMS_DEALER_ACCEPTS_GIFTS:0;
		ArmsDealerInfo[i].uiFlags |= GetPrivateProfileInt(tsSectionName,_T("someuseditems"),0, tsFullIniPath)?ARMS_DEALER_SOME_USED_ITEMS:0;
		ArmsDealerInfo[i].uiFlags |= GetPrivateProfileInt(tsSectionName,_T("hasnoinventory"),0, tsFullIniPath)?ARMS_DEALER_HAS_NO_INVENTORY:0;
		Edit_ReplaceSel(hConsole,".");
	}
	Edit_ReplaceSel(hConsole,"done");

	// Get dealers' inventory 
	Edit_ReplaceSel(hConsole,"\r\nloading_dealers_inventory");
	_tcscpy(tsSectionName,DEALERITEM_TAG);
	for( i=0; 1; i++ )
	{
		_itot( i, tsSectionName+_tcslen(DEALERITEM_TAG), 10);
		if( !(npc = GetPrivateProfileInt(tsSectionName,_T("npc"),0, tsFullIniPath)) )
			break;
		for( iDealer=0; iDealer<NUM_ARMS_DEALERS && ArmsDealerInfo[iDealer].ubShopKeeperID != npc; iDealer++ );
		if( iDealer>=NUM_ARMS_DEALERS )
			break;

		gDealerInventory[iDealer][gDealerInvCount[iDealer]].sItemIndex = GetPrivateProfileInt(tsSectionName,_T("item"),0, tsFullIniPath);
		gDealerInventory[iDealer][gDealerInvCount[iDealer]].ubOptimalNumber = GetPrivateProfileInt(tsSectionName,_T("max"),0, tsFullIniPath);

		gDealerInvCount[iDealer]++;
		Edit_ReplaceSel(hConsole,".");
	}
	Edit_ReplaceSel(hConsole,"done");
	return 1;
}
//</SB>

INT8 GetDealersMaxItemAmount( UINT8 ubDealerID, UINT16 usItemIndex )
{
//SB
	int i;
	for(i=0; i<gDealerInvCount[ubDealerID]; i++)
		if(gDealerInventory[ubDealerID][i].sItemIndex==usItemIndex)
			return gDealerInventory[ubDealerID][i].ubOptimalNumber;
	return 0;
}

DEALER_POSSIBLE_INV *GetPointerToDealersPossibleInventory( UINT8 ubArmsDealerID )
{
	return gDealerInventory[ubArmsDealerID];
}


UINT8 GetCurrentSuitabilityForItem( INT8 bArmsDealer, UINT16 usItemIndex )
{
	UINT8 ubItemCoolness;
	UINT8 ubMinCoolness, ubMaxCoolness;


	// item suitability varies with the player's maximum progress through the game.  The farther he gets, the better items
	// we make available.  Weak items become more and more infrequent later in the game, although they never quite vanish.

	// items illegal in this game are unsuitable [this checks guns vs. current GunSet!]
	if (!ItemIsLegal( usItemIndex ))
	{
		return(ITEM_SUITABILITY_NONE);
	}

	// items normally not sold at shops are unsuitable
	if ( Item[ usItemIndex ].fFlags & ITEM_NOT_BUYABLE )
	{
		return(ITEM_SUITABILITY_NONE);
	}


	ubItemCoolness = Item[ usItemIndex ].ubCoolness;

	if (ubItemCoolness == 0)
	{
		// items without a coolness rating can't be sold to the player by shopkeepers
		return(ITEM_SUITABILITY_NONE);
	}

	// the following staple items are always deemed highly suitable regardless of player's progress:
	switch (usItemIndex)
	{
		case CLIP38_6:
		case CLIP9_15:
		case CLIP9_30:
		case CLIP357_6:
		case CLIP357_9:
		case CLIP45_7:
		case CLIP45_30:
		case CLIP12G_7:
		case CLIP12G_7_BUCKSHOT:
		case CLIP545_30_HP:
		case CLIP556_30_HP:
		case CLIP762W_10_HP:
		case CLIP762W_30_HP:
		case CLIP762N_5_HP:
		case CLIP762N_20_HP:

		case FIRSTAIDKIT:
		case MEDICKIT:
		case TOOLKIT:
		case LOCKSMITHKIT:

		case CANTEEN:
		case CROWBAR:
		case JAR:
		case JAR_ELIXIR:
		case JAR_CREATURE_BLOOD:

			return(ITEM_SUITABILITY_ALWAYS);
	}


	// If it's not BobbyRay, Tony, or Devin
	if ((bArmsDealer != -1) && (bArmsDealer != ARMS_DEALER_TONY) && (bArmsDealer != ARMS_DEALER_DEVIN))
	{
		// all the other dealers have very limited inventories, so their suitability remains constant at all times in game
		return(ITEM_SUITABILITY_HIGH);
	}


	// figure out the appropriate range of coolness based on player's maximum progress so far

	ubMinCoolness = HighestPlayerProgressPercentage() / 10;
	ubMaxCoolness = ( HighestPlayerProgressPercentage() / 10 ) + 1;

	// Tony has the better stuff sooner (than Bobby R's)
	if (bArmsDealer == ARMS_DEALER_TONY)
	{
		ubMinCoolness += 1;
		ubMaxCoolness += 1;
	}
	else if (bArmsDealer == ARMS_DEALER_DEVIN)
	{
		// almost everything Devin sells is pretty cool (4+), so gotta apply a minimum or he'd have nothing early on
		if ( ubMinCoolness < 3 )
		{
			ubMinCoolness = 3;
			ubMaxCoolness = 4;
		}
	}


	ubMinCoolness = max( 1, min( 9, ubMinCoolness ) );
	ubMaxCoolness = max( 2, min( 10, ubMaxCoolness ) );


	// if item is too cool for current level of progress
	if (ubItemCoolness > ubMaxCoolness)
	{
		return(ITEM_SUITABILITY_NONE);
	}

	// if item is exactly within the current coolness window
	if ((ubItemCoolness >= ubMinCoolness) && (ubItemCoolness <= ubMaxCoolness))
	{
		return(ITEM_SUITABILITY_HIGH);
	}

	// if item is still relatively close to low end of the window
	if ((ubItemCoolness + 2) >= ubMinCoolness)
	{
		return(ITEM_SUITABILITY_MEDIUM);
	}

	// item is way uncool for player's current progress, but it's still possible for it to make an appearance
	return(ITEM_SUITABILITY_LOW);
}



UINT8 ChanceOfItemTransaction( INT8 bArmsDealer, UINT16 usItemIndex, BOOLEAN fDealerIsSelling, BOOLEAN fUsed )
{
	UINT8 ubItemCoolness;
	UINT8 ubChance = 0;
	BOOLEAN fBobbyRay = FALSE;


	// make sure dealers don't carry used items that they shouldn't
	if ( fUsed && !fDealerIsSelling && !CanDealerItemBeSoldUsed( usItemIndex ) )
		return( 0 );


	if (bArmsDealer == -1)
	{
		// Bobby Ray has an easier time getting resupplied than the local dealers do
		fBobbyRay = TRUE;
	}

	ubItemCoolness = Item[ usItemIndex ].ubCoolness;

	switch (GetCurrentSuitabilityForItem( bArmsDealer, usItemIndex ) )
	{
		case ITEM_SUITABILITY_NONE:
			if (fDealerIsSelling)
			{
				// dealer always gets rid of stuff that is too advanced or inappropriate ASAP
				ubChance = 100;
			}
			else // dealer is buying
			{
				// can't get these at all
				ubChance = 0;
			}
			break;

		case ITEM_SUITABILITY_LOW:
			ubChance = (fBobbyRay) ? 25 : 15;
			break;

		case ITEM_SUITABILITY_MEDIUM:
			ubChance = (fBobbyRay) ? 50 : 30;
			break;

		case ITEM_SUITABILITY_HIGH:
			ubChance = (fBobbyRay) ? 75 : 50;
			break;

		case ITEM_SUITABILITY_ALWAYS:
			if (fDealerIsSelling)
			{
				// sells just like suitability high
				ubChance = 75;
			}
			else // dealer is buying
			{
				// dealer can always get a (re)supply of these
				ubChance = 100;
			}
			break;

		default:
			Assert(0);
			break;
	}


	// if there's any uncertainty
	if ((ubChance > 0) && (ubChance < 100))
	{
		// cooler items sell faster
		if (fDealerIsSelling)
		{
			ubChance += (5 * ubItemCoolness);

			// ARM: New - keep stuff on the shelves longer
			ubChance /= 2;
		}

		// used items are traded more rarely
		if (fUsed)
		{
			ubChance /= 2;
		}
	}


	return(ubChance);
}



BOOLEAN ItemTransactionOccurs( INT8 bArmsDealer, UINT16 usItemIndex, BOOLEAN fDealerIsSelling, BOOLEAN fUsed )
{
	UINT8 ubChance;
	INT16 sInventorySlot;


	ubChance = ChanceOfItemTransaction( bArmsDealer, usItemIndex, fDealerIsSelling, fUsed );

	// if the dealer is buying, and a chance exists (i.e. the item is "eligible")
	if (!fDealerIsSelling && (ubChance > 0))
	{
		// mark it as such
		if (bArmsDealer == -1)
		{
			if (fUsed)
			{
				sInventorySlot = GetInventorySlotForItem(LaptopSaveInfo.BobbyRayUsedInventory, usItemIndex, fUsed);
				LaptopSaveInfo.BobbyRayUsedInventory[ sInventorySlot ].fPreviouslyEligible = TRUE;
			}
			else
			{
				sInventorySlot = GetInventorySlotForItem(LaptopSaveInfo.BobbyRayInventory, usItemIndex, fUsed);
				LaptopSaveInfo.BobbyRayInventory    [ sInventorySlot ].fPreviouslyEligible = TRUE;
			}
		}
		else
		{
			gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].fPreviouslyEligible = TRUE;
		}
	}

	// roll to see if a transaction occurs
	if (Random(100) < ubChance)
	{
		return(TRUE);
	}
	else
	{
		return(FALSE);
	}
}



UINT8 DetermineInitialInvItems( INT8 bArmsDealerID, UINT16 usItemIndex, UINT8 ubChances, BOOLEAN fUsed)
{
	UINT8 ubNumBought;
	UINT8 ubCnt;

	// initial inventory is now rolled for one item at a time, instead of one type at a time, to improve variety
	ubNumBought = 0;
	for (ubCnt = 0; ubCnt < ubChances; ubCnt++)
	{
		if (ItemTransactionOccurs( bArmsDealerID, usItemIndex, DEALER_BUYING, fUsed))
		{
			ubNumBought++;
		}
	}

	return( ubNumBought );
}



UINT8 HowManyItemsAreSold( INT8 bArmsDealerID, UINT16 usItemIndex, UINT8 ubNumInStock, BOOLEAN fUsed)
{
	UINT8 ubNumSold;
	UINT8 ubCnt;

	// items are now virtually "sold" one at a time
	ubNumSold = 0;
	for (ubCnt = 0; ubCnt < ubNumInStock; ubCnt++)
	{
		if (ItemTransactionOccurs( bArmsDealerID, usItemIndex, DEALER_SELLING, fUsed))
		{
			ubNumSold++;
		}
	}

	return( ubNumSold );
}



UINT8 HowManyItemsToReorder(UINT8 ubWanted, UINT8 ubStillHave)
{
	UINT8 ubNumReordered;

	Assert(ubStillHave <= ubWanted);

	ubNumReordered = ubWanted - ubStillHave;

	//randomize the amount. 33% of the time we add to it, 33% we subtract from it, rest leave it alone
	switch (Random(3))
	{
		case 0:
			ubNumReordered += ubNumReordered / 2;
			break;
		case 1:
			ubNumReordered -= ubNumReordered / 2;
			break;
	}

	return(ubNumReordered);
}



int BobbyRayItemQsortCompare(const void *pArg1, const void *pArg2)
{
	UINT16	usItem1Index;
	UINT16	usItem2Index;
	UINT8		ubItem1Quality;
	UINT8		ubItem2Quality;

	usItem1Index = ( ( STORE_INVENTORY * ) pArg1 ) -> usItemIndex;
	usItem2Index = ( ( STORE_INVENTORY * ) pArg2 ) -> usItemIndex;

	ubItem1Quality = ( ( STORE_INVENTORY * ) pArg1 ) -> ubItemQuality;
	ubItem2Quality = ( ( STORE_INVENTORY * ) pArg2 ) -> ubItemQuality;

	return( CompareItemsForSorting( usItem1Index, usItem2Index, ubItem1Quality, ubItem2Quality ) );
}



int ArmsDealerItemQsortCompare(const void *pArg1, const void *pArg2)
{
	UINT16	usItem1Index;
	UINT16	usItem2Index;
	UINT8		ubItem1Quality;
	UINT8		ubItem2Quality;

	usItem1Index = ( ( INVENTORY_IN_SLOT * ) pArg1 ) -> sItemIndex;
	usItem2Index = ( ( INVENTORY_IN_SLOT * ) pArg2 ) -> sItemIndex;

	ubItem1Quality = ( ( INVENTORY_IN_SLOT * ) pArg1 ) -> ItemObject.bStatus[ 0 ];
	ubItem2Quality = ( ( INVENTORY_IN_SLOT * ) pArg2 ) -> ItemObject.bStatus[ 0 ];

	return( CompareItemsForSorting( usItem1Index, usItem2Index, ubItem1Quality, ubItem2Quality ) );
}



int RepairmanItemQsortCompare(const void *pArg1, const void *pArg2)
{
	INVENTORY_IN_SLOT* pInvSlot1;
	INVENTORY_IN_SLOT* pInvSlot2;
	UINT32	uiRepairTime1;
	UINT32	uiRepairTime2;


	pInvSlot1 = ( INVENTORY_IN_SLOT * ) pArg1;
	pInvSlot2 = ( INVENTORY_IN_SLOT * ) pArg2;

	Assert( pInvSlot1->sSpecialItemElement != -1);
	Assert( pInvSlot2->sSpecialItemElement != -1);

	uiRepairTime1 = gArmsDealersInventory[ gbSelectedArmsDealerID ][ pInvSlot1->sItemIndex ].SpecialItem[ pInvSlot1->sSpecialItemElement ].uiRepairDoneTime;
	uiRepairTime2 = gArmsDealersInventory[ gbSelectedArmsDealerID ][ pInvSlot2->sItemIndex ].SpecialItem[ pInvSlot2->sSpecialItemElement ].uiRepairDoneTime;


	// lower reapir time first
  if ( uiRepairTime1 < uiRepairTime2 )
  {
    return( -1 );
  }
  else
  if ( uiRepairTime1 > uiRepairTime2 )
  {
    return( 1 );
  }
  else
  {
    return( 0 );
	}
}



int CompareItemsForSorting( UINT16 usItem1Index, UINT16 usItem2Index, UINT8 ubItem1Quality, UINT8 ubItem2Quality )
{
	UINT8		ubItem1Category;
	UINT8		ubItem2Category;
	UINT16	usItem1Price;
	UINT16	usItem2Price;
	UINT8		ubItem1Coolness;
	UINT8		ubItem2Coolness;

	ubItem1Category = GetDealerItemCategoryNumber( usItem1Index );
	ubItem2Category = GetDealerItemCategoryNumber( usItem2Index );

	// lower category first
  if ( ubItem1Category < ubItem2Category )
  {
    return( -1 );
  }
  else
  if ( ubItem1Category > ubItem2Category )
  {
    return( 1 );
  }
  else
  {
		// the same category 
		if ( Item[ usItem1Index ].usItemClass == IC_AMMO && Item[ usItem2Index ].usItemClass == IC_AMMO )
		{
			UINT8		ubItem1Calibre;
			UINT8		ubItem2Calibre;
			UINT8		ubItem1MagSize;
			UINT8		ubItem2MagSize;

			// AMMO is sorted by caliber first
			ubItem1Calibre = Magazine[ Item[ usItem1Index ].ubClassIndex ].ubCalibre;
			ubItem2Calibre = Magazine[ Item[ usItem2Index ].ubClassIndex ].ubCalibre;
			if ( ubItem1Calibre > ubItem2Calibre )
			{
				return( -1 );
			}
			else
			if ( ubItem1Calibre < ubItem2Calibre )
			{
				return( 1 );
			}
			// the same caliber - compare size of magazine, then fall out of if statement
			ubItem1MagSize = Magazine[ Item[ usItem1Index ].ubClassIndex ].ubMagSize;
			ubItem2MagSize = Magazine[ Item[ usItem2Index ].ubClassIndex ].ubMagSize;
			if ( ubItem1MagSize > ubItem2MagSize )
			{
				return( -1 );
			}
			else
			if ( ubItem1MagSize < ubItem2MagSize )
			{
				return( 1 );
			}

		}
		else
		{
			// items other than ammo are compared on coolness first
			ubItem1Coolness = Item[ usItem1Index ].ubCoolness;
			ubItem2Coolness = Item[ usItem2Index ].ubCoolness;

			// higher coolness first
			if ( ubItem1Coolness > ubItem2Coolness )
			{
				return( -1 );
			}
			else
			if ( ubItem1Coolness < ubItem2Coolness )
			{
				return( 1 );
			}
		}

		// the same coolness/caliber - compare base prices then
		usItem1Price = Item[ usItem1Index ].usPrice;
		usItem2Price = Item[ usItem2Index ].usPrice;

		// higher price first
		if ( usItem1Price > usItem2Price )
		{
			return( -1 );
		}
		else
		if ( usItem1Price < usItem2Price )
		{
			return( 1 );
		}
		else
		{
			// the same price - compare item #s, then

			// lower index first
			if ( usItem1Index < usItem2Index )
			{
				return( -1 );
			}
			else
			if ( usItem1Index > usItem2Index )
			{
				return( 1 );
			}
			else
			{
				// same item type = compare item quality, then

				// higher quality first
				if ( ubItem1Quality > ubItem2Quality )
				{
					return( -1 );
				}
				else
				if ( ubItem1Quality < ubItem2Quality )
				{
					return( 1 );
				}
				else
				{
					// identical items!
					return( 0 );
				}
			}
		}
  }
}



UINT8 GetDealerItemCategoryNumber( UINT16 usItemIndex )
{
	UINT32	uiItemClass;
	UINT8		ubWeaponClass;
	UINT8		ubCategory = 0;


	uiItemClass  = Item[ usItemIndex ].usItemClass;

//	if ( usItemIndex < /*MAX_WEAPONS*/MAX_WEAPON_TYPES )
	if ( Item[usItemIndex].usItemClass & IC_WEAPON )
	{
		ubWeaponClass = Weapon[ Item[usItemIndex].ubClassIndex ].ubWeaponClass;
	}
	else
	{
		// not a weapon, so no weapon class, this won't be needed
		ubWeaponClass = 0;
	}


	ubCategory = 0;

	// search table until end-of-list marker is encountered
	while ( DealerItemSortInfo[ ubCategory ].uiItemClass != IC_NONE )
	{
		if ( DealerItemSortInfo[ ubCategory ].uiItemClass == uiItemClass )
		{
			// if not a type of gun
			if ( uiItemClass != IC_GUN )
			{
				// then we're found it
				return ( ubCategory );
			}
			else
			{
				// for guns, must also match on weapon class
				if ( DealerItemSortInfo[ ubCategory ].ubWeaponClass == ubWeaponClass )
				{
					// then we're found it
					return ( ubCategory );
				}
			}
		}

		// check vs. next category in the list
		ubCategory++;
	}

	// should never be trying to locate an item that's not covered in the table!
	Assert( FALSE );
	return( 0 );
}



BOOLEAN CanDealerItemBeSoldUsed( UINT16 usItemIndex )
{
	if ( !( Item[ usItemIndex ].fFlags & ITEM_DAMAGEABLE ) )
		return(FALSE);

	// certain items, although they're damagable, shouldn't be sold in a used condition
	return( DealerItemSortInfo[ GetDealerItemCategoryNumber( usItemIndex ) ].fAllowUsed );
}
